package com.fanvil.videoconference;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.graphics.SurfaceTexture.OnFrameAvailableListener;
import android.os.Handler;
import android.os.Process;
import android.util.AttributeSet;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.opengl.EGLContext;
import android.opengl.GLES20;

import android.util.Log;
import android.opengl.EGL14;

import java.io.Writer;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;

import java.util.ArrayList;
import java.util.concurrent.Semaphore;

//import vdroid.api.internal.platform.media.FvlSurfaceManager;
//import vdroid.api.internal.platform.media.FvlTextureManager;
import com.fanvil.videoconference.FvlLogger;

public class FvlVideoConsumerBase {
    private static FvlLogger sLogger = FvlLogger.getLogger(FvlVideoConsumerBase.class.getSimpleName(), FvlLogger.DEBUG);
    private final static boolean LOG_ATTACH_DETACH = true;
    private final static boolean LOG_THREADS = true;
    private final static boolean LOG_STOP_START = true;
    private final static boolean LOG_SURFACE = true;
    private final static boolean LOG_RENDERER = true;
    private final static boolean LOG_RENDERER_DRAW_FRAME = true;
    private final static boolean LOG_EGL = true;
    private final static boolean LOG_GL = true;

    /**
     * The renderer only renders
     * when the surface is created, or when {@link #requestRender} is called.
     *
     * @see #getRenderMode()
     * @see #setRenderMode(int)
     * @see #requestRender()
     */
    public final static int RENDERMODE_WHEN_DIRTY = 0;
    /**
     * The renderer is called
     * continuously to re-render the scene.
     *
     * @see #getRenderMode()
     * @see #setRenderMode(int)
     */
    public final static int RENDERMODE_CONTINUOUSLY = 1;

    /**
     * Check glError() after every GL call and throw an exception if glError indicates
     * that an error has occurred. This can be used to help track down which OpenGL ES call
     * is causing an error.
     *
     * @see #getDebugFlags
     * @see #setDebugFlags
     */
    public final static int DEBUG_CHECK_GL_ERROR = 1;

    /**
     * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
     *
     * @see #getDebugFlags
     * @see #setDebugFlags
     */
    public final static int DEBUG_LOG_GL_CALLS = 2;

    private String mTag = null;
    private String mName = null;
    private final WeakReference<FvlVideoConsumerBase> mThisWeakRef =
            new WeakReference<FvlVideoConsumerBase>(this);
    private GLThread mGLThread = null;
    private Renderer mRenderer;
    private EGLFactory mEGLFactory;
    private GLFactory mGLFactory;
    //private boolean mPreserveEGLContextOnPause = false;

    private static final GLThreadManager sGLThreadManager = new GLThreadManager();

    public FvlVideoConsumerBase() {
        if (sLogger.isLoggable()) sLogger.d("Created");
    }

    public FvlVideoConsumerBase(String name) {
        mTag = name;
        mName = name;
        if (sLogger.isLoggable()) sLogger.d("[" + mName + "] Created");
    }

    protected void surfaceCreated() {
        if (mGLThread != null) {
            mGLThread.surfaceCreated();
        }
    }

    protected void surfaceDestroyed() {
        if (mGLThread != null) {
            mGLThread.surfaceDestroyed();
        }
    }

    /*public SurfaceTexture getSurfaceTexture() {
        if (mGLThread != null) {
            return mGLThread.getSurfaceTexture();
        }
        return null;
    }*/

    /*public Surface getSurface() {
        if (mGLThread != null) {
            return mGLThread.getSurface();
        }
        return null;
    }*/

    public void requestRender() {
        if (mGLThread != null) {
            mGLThread.requestRender();
        }
    }

    public void init() {
        if (mGLThread == null) {
            mGLThread = new GLThread(mName, mThisWeakRef);
        }
        if (mGLThread != null) {
            mGLThread.start();
        }
    }

    public void start() {
        checkAndStart();
    }

    public void stop() {
        if (mGLThread != null && !mGLThread.isExited() && !mGLThread.isStopped()) {
            mGLThread.onStop();
        }
    }

    public void release() {
        if (mGLThread != null) {
            mGLThread.requestRelease();
        }
    }

    public void checkAndStart() {
        sLogger.d("checkAndStart");
        if (mGLThread == null) {
            mGLThread = new GLThread(mName, mThisWeakRef);
        }
        if (mGLThread != null) {
            if (mGLThread.isExited()) {
                mGLThread.start();
            } else if (mGLThread.isStopped()) {
                mGLThread.onStart();
            }
        }
    }

    public void setRenderer(Renderer renderer) {
        sLogger.d("setRenderer");
        checkRenderThreadState();
        mRenderer = renderer;
    }

    public void setEGLFactory(EGLFactory factory) {
        sLogger.d("setEGLFactory");
        checkRenderThreadState();
        mEGLFactory = factory;
    }

    public void setGLFactory(GLFactory factory) {
        sLogger.d("setGLFactory");
        checkRenderThreadState();
        mGLFactory = factory;
    }

    public interface Renderer {
        boolean onDrawFrame();
    }

    public interface EGLFactory {
        boolean createRenderTarget();
    }

    public interface GLFactory {
        boolean createTextureRenderer(); 
        boolean createTextureSource();
    }

    /**
     * A generic GL Thread. Takes care of initializing EGL and GL. Delegates
     * to a Renderer instance to do the actual drawing. Can be configured to
     * render continuously or on request.
     *
     * All potentially blocking synchronization is done through the
     * sGLThreadManager object. This avoids multiple-lock ordering issues.
     *
     */
    static class GLThread extends Thread {
        GLThread(String name, WeakReference<FvlVideoConsumerBase> FvlVideoConsumerBaseWeakRef) {
            super();
            sLogger.d("[" + mName + "] GLThread create begin");
            mName = name;
            mTag = name;
            mWidth = 0;
            mHeight = 0;
            mRequestRender = true;
            mRenderMode = RENDERMODE_WHEN_DIRTY;
            mWantRenderNotification = false;
            mFvlVideoConsumerBaseWeakRef = FvlVideoConsumerBaseWeakRef;
            sLogger.d("[" + mName + "] GLThread create end");
        }

        @Override
        public void run() {
            setName("GLThread " + getId());
            sLogger.d("[" + mName + "] GLThread running");

            Process.setThreadPriority(-10); // Process.THREAD_PRIORITY_VIDEO
            if (LOG_THREADS) {
                sLogger.d("[" + mName + "]  starting tid=" + getId());
            }

            try {
                guardedRun();
            } catch (InterruptedException e) {
                // fall thru and exit normally
            } finally {
                sGLThreadManager.threadExiting(this);
            }
            sLogger.d("[" + mName + "] GLThread exit");
        }

        private void guardedRun() throws InterruptedException {
            mHaveEglContext = false;
            mHaveEglSurface = false;
            mHaveGlContext = false;
            //mHasSurfaceTexture = false;
            mWantRenderNotification = false;
            
            FvlVideoConsumerBase base1 = mFvlVideoConsumerBaseWeakRef.get();
            if (base1 != null && base1.mEGLFactory != null) {
                base1.mEGLFactory.createRenderTarget();
            }
            if (base1 != null && base1.mGLFactory != null) {
                base1.mGLFactory.createTextureRenderer();
                base1.mGLFactory.createTextureSource();
            }

            try {
                boolean createEglContext = false;
                boolean createEglSurface = false;
                boolean createGlContext = false;
                boolean createSurfaceTexture = false;
                boolean lostEglContext = false;
                boolean sizeChanged = false;
                boolean wantRenderNotification = false;
                boolean doRenderNotification = false;
                boolean askedToReleaseEglContext = false;
                boolean askedToReleaseGlContext = false;
                int w = 0;
                int h = 0;
                Runnable event = null;
                Runnable finishDrawingRunnable = null;
                int swapError = EGL14.EGL_SUCCESS;

                while (true) {
                    synchronized (sGLThreadManager) {
                        while (true) {
                            if (mShouldExit) {
                                if (sLogger.isLoggable()) sLogger.d("[" + mName + "] mShouldExit ");
                                return;
                            }

                            //if (! mEventQueue.isEmpty()) {
                            //    event = mEventQueue.remove(0);
                            //    break;
                            //}

                            // Update the stop state.
                            boolean stopping = false;
                            if (mStopped != mRequestStopped) {
                                stopping = mRequestStopped;
                                mStopped = mRequestStopped;
                                sGLThreadManager.notifyAll();
                                if (LOG_STOP_START) {
                                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  mStopped is now " + mStopped + " tid=" + getId());
                                }
                            }

                            if (mShouldReleaseGlContext) {
                                if (LOG_SURFACE) {
                                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  releasing GL context because asked to tid=" + getId());
                                }
                                //stopSurfaceTextureLocked();
                                //stopGlContextLocked();
                                //stopShareGlContextLocked();
                                mShouldReleaseGlContext = false;
                                askedToReleaseGlContext = true;
                                //askedToReleaseShareGlContext = true;
                            }

                            // Do we need to give up the EGL context?
                            if (mShouldReleaseEglContext) {
                                if (LOG_SURFACE) {
                                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  releasing EGL context because asked to tid=" + getId());
                                }
                                //stopEglShareSurfaceLocked();
                                //stopEglShareContextLocked();
                                //stopEglSurfaceLocked();
                                //stopEglContextLocked();
                                mShouldReleaseEglContext = false;
                                askedToReleaseEglContext = true;
                                //askedToReleaseEglShareContext = true;
                            }

                            // Have we lost the EGL context?
                            if (lostEglContext) {
                                //stopEglSurfaceLocked();
                                //stopEglContextLocked();
                                lostEglContext = false;
                                if (sLogger.isLoggable()) sLogger.d("[" + mName + "] lostEglContext ");
                            }

                            // Have we lost the EGL share context?
                            //if (lostEglShareContext) {
                                //stopEglShareSurfaceLocked();
                                //stopEglShareContextLocked();
                                //lostEglShareContext = false;
                                //if (sLogger.isLoggable()) sLogger.d("[" + mName + "] lostEglShareContext ");
                            //}

                            if (stopping) {
                                //When stopping, release the SurfaceTexture:
                                //if (mHasSurfaceTexture) {
                                //    if (LOG_GL) {
                                //        if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  releasing SurfaceTexture because stopped tid=" + getId());
                                //    }
                                    //stopSurfaceTextureLocked();
                                //}

                                //When stopping, release the GL Context:
                                //if (mHaveGlContext) {
                                //    if (LOG_GL) {
                                //        if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  releasing GL context because stopped tid=" + getId());
                                //    }
                                    //stopGlContextLocked();
                                //}

                                //When stopping, release the share GL Context:
                                //if (mHaveShareGlContext) {
                                    //if (LOG_GL) {
                                        //if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  releasing GL context because stopped tid=" + getId());
                                    //}
                                    //stopShareGlContextLocked();
                                //}

                                // When stopping, release the EGL share surface:
                                //if (mHaveEglShareSurface) {
                                    //if (LOG_SURFACE) {
                                     //   if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  releasing EGL share surface because stopped tid=" + getId());
                                    //}
                                    //stopEglShareSurfaceLocked();
                                //}

                                // When stopping, release the EGL Share Context:
                                //if (mHaveEglShareContext) {
                                    //stopEglShareContextLocked();
                                    //if (LOG_SURFACE) {
                                    //    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  releasing EGL share context because stopped tid=" + getId());
                                    //}
                                //}

                                // When stopping, release the EGL surface:
                                if (mHaveEglSurface) {
                                    if (LOG_SURFACE) {
                                        if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  releasing EGL surface because stopped tid=" + getId());
                                    }
                                    //stopEglSurfaceLocked();
                                }

                                // When stopping, optionally release the EGL Context:
                                if (mHaveEglContext) {
                                    //stopEglContextLocked();
                                    if (LOG_SURFACE) {
                                        if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  releasing EGL context because stopped tid=" + getId());
                                    }
                                }
                            }

                            // Have we lost the SurfaceView surface?
                            if ((! mHasSurface) && (! mWaitingForSurface)) {
                                if (LOG_SURFACE) {
                                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  noticed surfaceView surface lost tid=" + getId());
                                }
                                if (mHaveEglSurface) {
                                    //stopEglSurfaceLocked();
                                }
                                mWaitingForSurface = true;
                                mSurfaceIsBad = false;
                                sGLThreadManager.notifyAll();
                            }

                            // Have we lost the Share surface?
                            /*if ((! mHasShareSurface) && (! mWaitingForShareSurface)) {
                                if (LOG_SURFACE) {
                                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  noticed share surface lost tid=" + getId());
                                }
                                if (mHaveEglShareSurface) {
                                    //stopEglShareSurfaceLocked();
                                }
                                mWaitingForShareSurface = true;
                                mShareSurfaceIsBad = false;
                                sGLThreadManager.notifyAll();
                            }*/

                            // Have we acquired the surface view surface?
                            if (mHasSurface && mWaitingForSurface) {
                                if (LOG_SURFACE) {
                                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  noticed surfaceView surface acquired tid=" + getId());
                                }
                                mWaitingForSurface = false;
                                sGLThreadManager.notifyAll();
                            }

                            // Have we acquired the share surface?
                            /*if (mHasShareSurface && mWaitingForShareSurface) {
                                if (LOG_SURFACE) {
                                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  noticed share surface acquired tid=" + getId());
                                }
                                mWaitingForShareSurface = false;
                                sGLThreadManager.notifyAll();
                            }*/

                            // If we don't have an EGL context, try to acquire one.
                            if (! mHaveEglContext || mSurfaceIsUpdate) {
                                if (sLogger.isLoggable()) sLogger.d("[" + mName + "] ! mHaveEglContext ");
                                if (askedToReleaseEglContext) {
                                    askedToReleaseEglContext = false;
                                } else {
                                    /*try {
                                        //setupEglManagerLocked(mName);
                                    } catch (RuntimeException t) {
                                        sGLThreadManager.releaseEglContextLocked(this);
                                        throw t;
                                    }*/
                                    //getRenderTarget();
                                    FvlVideoConsumerBase base = mFvlVideoConsumerBaseWeakRef.get();
                                    if (base != null && base.mEGLFactory != null) {
                                        base.mEGLFactory.createRenderTarget();
                                    }
                                    mHaveEglContext = true;
                                    mSurfaceIsUpdate = false;
                                    sGLThreadManager.notifyAll();
                                }
                            }

                            // If we don't have an EGL share context, try to acquire one.
                            /*if (! mHaveEglShareContext) {
                                if (sLogger.isLoggable()) sLogger.d("[" + mName + "] ! mHaveEglShareContext ");

                                if (askedToReleaseEglShareContext) {
                                    askedToReleaseEglShareContext = false;
                                } else {
                                    try {
                                        setupShareEglManagerLocked("sharedisplay");
                                    } catch (RuntimeException t) {
                                        sGLThreadManager.releaseEglShareContextLocked(this);
                                        throw t;
                                    }
                                    sGLThreadManager.notifyAll();
                                }
                            }*/

                            if (! mHaveGlContext) {
                                if (sLogger.isLoggable()) sLogger.d("[" + mName + "] ! mHaveGlContext");
                                if (askedToReleaseGlContext) {
                                    askedToReleaseGlContext = false;
                                } else {
                                    /*try {
                                        //EGLSurface must be created
                                        mEglManager.makeCurrent();
                                    } catch (RuntimeException e) {
                                        e.printStackTrace();
                                    }
                                    setupGlManagerLocked(mTag);*/
                                    /*if (mRenderTarget != null) {
                                        mRenderTarget.focus();
                                        setupSurfaceTexture();
                                    }*/
                                    FvlVideoConsumerBase base = mFvlVideoConsumerBaseWeakRef.get();
                                    boolean result = false;
                                    if (base != null && base.mGLFactory != null) {
                                        base.mGLFactory.createTextureRenderer();
                                        result = base.mGLFactory.createTextureSource();
                                    }
                                    mHaveGlContext = result;
                                    sGLThreadManager.notifyAll();
                                }
                            }

                            /*if (! mHaveShareGlContext) {
                                if (sLogger.isLoggable()) sLogger.d("[" + mName + "] ! mHaveShareGlContext");
                                if (askedToReleaseShareGlContext) {
                                    askedToReleaseShareGlContext = false;
                                } else {
                                    try {
                                        //EGLSurface must be created
                                        mShareEglManager.makeCurrent();
                                    } catch (RuntimeException e) {
                                        e.printStackTrace();
                                    }
                                    setupShareGlManagerLocked(mTag);
                                    sGLThreadManager.notifyAll();
                                }
                            }*/

                            // Ready to draw?
                            if (readyToDraw()) {
                                if (mHasSurface && mHaveEglContext && !mHaveEglSurface) {
                                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "] mHaveEglContext && !mHaveEglSurface");
                                    mHaveEglSurface = true;
                                    createEglSurface = true;
                                    sizeChanged = true;
                                }

                                /*if (mHasShareSurface && mHaveEglShareContext && !mHaveEglShareSurface) {
                                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "] mHaveEglShareContext && !mHaveEglShareSurface");
                                    mHaveEglShareSurface = true;
                                    createEglShareSurface = true;
                                }*/

                                if (mHaveEglSurface /*|| mHaveEglShareSurface*/) {
                                    if (mSizeChanged) {
                                        sizeChanged = true;
                                        w = mWidth;
                                        h = mHeight;
                                        mWantRenderNotification = true;
                                        if (LOG_SURFACE) {
                                            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] noticing that we want render notification tid="
                                                    + getId());
                                        }

                                        // Destroy and recreate the EGL surface.
                                        createEglSurface = true;

                                        mSizeChanged = false;
                                    }
                                    mRequestRender = false;
                                    sGLThreadManager.notifyAll();
                                    break;
                                }
                            }
                            // By design, this is the only place in a GLThread thread where we wait().
                            if (LOG_THREADS) {
                                if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  waiting tid=" + getId()
                                    + " mHaveEglContext: " + mHaveEglContext
                                    + " mHaveEglSurface: " + mHaveEglSurface
                                    //+ " mHaveEglShareContext: " + mHaveEglShareContext
                                    //+ " mHaveEglShareSurface: " + mHaveEglShareSurface
                                    + " mHaveGlContext: " + mHaveGlContext
                                    + " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface
                                    //+ " mFinishedCreatingEglShareSurface: " + mFinishedCreatingEglShareSurface
                                    + " mStopped: " + mStopped
                                    + " mHasSurface: " + mHasSurface
                                    //+ " mHasShareSurface: " + mHasShareSurface
                                    //+ " mHasSurfaceTexture: " + mHasSurfaceTexture
                                    //+ " mSurfaceIsUpdate: " + mSurfaceIsUpdate
                                    + " mSurfaceIsBad: " + mSurfaceIsBad
                                    //+ " mShareSurfaceIsBad: " + mShareSurfaceIsBad
                                    + " mWaitingForSurface: " + mWaitingForSurface
                                    //+ " mWaitingForShareSurface: " + mWaitingForShareSurface
                                    + " mWidth: " + mWidth
                                    + " mHeight: " + mHeight
                                    + " mRequestRender: " + mRequestRender
                                    + " mRenderMode: " + mRenderMode);
                            }
                            sGLThreadManager.wait();
                        }
                    } // end of synchronized(sGLThreadManager)

                    if (event != null) {
                        event.run();
                        event = null;
                        continue;
                    }

                    if (createEglSurface) {
                        if (LOG_SURFACE) {
                            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] egl createSurface");
                        }
                        /*if (mEglManager.createEGLSurface(mInputSurface)) {
                            if (mGlManager != null) {
                                mGlManager.setViewSize(mEglManager.getWidth(), mEglManager.getHeight());
                            }
                            synchronized(sGLThreadManager) {
                                mFinishedCreatingEglSurface = true;
                                sGLThreadManager.notifyAll();
                            }
                        } else {
                            sLogger.e("[" + mName + "] egl createSurface failed");
                            synchronized(sGLThreadManager) {
                                mFinishedCreatingEglSurface = true;
                                mSurfaceIsBad = true;
                                sGLThreadManager.notifyAll();
                            }
                            continue;
                        }*/
                        //mUIRenderTarget = mRenderTarget.forSurface(mInputSurface);
                        createEglSurface = false;
                            synchronized(sGLThreadManager) {
                                mFinishedCreatingEglSurface = true;
                                sGLThreadManager.notifyAll();
                            }
                    }

                    /*if (createEglShareSurface) {
                        if (LOG_SURFACE) {
                            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] egl createShareSurface");
                        }
                        if (mShareEglManager.createEGLSurface(mShareSurface)) {
                            synchronized(sGLThreadManager) {
                                mFinishedCreatingEglShareSurface = true;
                                sGLThreadManager.notifyAll();
                            }
                            createEglShareSurface = false;
                        } else {
                            sLogger.e("[" + mName + "] egl createShareSurface failed");
                            synchronized(sGLThreadManager) {
                                mFinishedCreatingEglShareSurface = true;
                                mShareSurfaceIsBad = true;
                                sGLThreadManager.notifyAll();
                            }
                        }

                    }*/
                    if (LOG_RENDERER_DRAW_FRAME) {
                        Log.w("GLThread", "onDrawFrame tid=" + getId());
                    }
                    {
                        FvlVideoConsumerBase base = mFvlVideoConsumerBaseWeakRef.get();
                        if (base != null) {
                            try {
                                base.mRenderer.onDrawFrame();
                            } finally {
                            }
                        }
                    }

                    /*if ((!mStopped) && mHasSurface && (!mSurfaceIsBad)
                            && mHaveEglContext && mHaveEglSurface && mHaveGlContext
                            && mInputSurface != null && mInputSurface.isValid()) {
                        if (LOG_RENDERER_DRAW_FRAME) {
                            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] onDrawFrame tid=" + getId());
                        }

                        swapError = drawFrame();
                        switch (swapError) {
                            case EGL14.EGL_SUCCESS:
                                break;
                            case EGL14.EGL_CONTEXT_LOST:
                                if (LOG_SURFACE) {
                                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  egl context lost tid=" + getId());
                                }
                                lostEglContext = true;
                                break;
                            default:
                                // Other errors typically mean that the current surface is bad,
                                // probably because the SurfaceView surface has been destroyed,
                                // but we haven't been notified yet.
                                // Log the error to help developers understand why rendering stopped.
                                sLogger.e("[" + mName + "]  share context eglSwapBuffers swapError=" + swapError);

                                synchronized(sGLThreadManager) {
                                    mSurfaceIsBad = true;
                                    sGLThreadManager.notifyAll();
                                }
                                break;
                        }
                    } else {
                        if (LOG_RENDERER_DRAW_FRAME) {
                            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] can NOT draw frame tid=" + getId());
                        }
                    }*/

                    /*if ((!mStopped) && mHasShareSurface && (!mShareSurfaceIsBad)
                            && mHaveEglShareContext && mHaveEglShareSurface && mHaveGlContext
                            && mShareSurface != null && mShareSurface.isValid()) {
                        swapError = drawShareFrame();
                        switch (swapError) {
                            case EGL14.EGL_SUCCESS:
                                break;
                            case EGL14.EGL_CONTEXT_LOST:
                                if (LOG_SURFACE) {
                                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  egl share context lost tid=" + getId());
                                }
                                lostEglShareContext = true;
                                break;
                            default:
                                // Other errors typically mean that the current surface is bad,
                                // probably because the SurfaceView surface has been destroyed,
                                // but we haven't been notified yet.
                                // Log the error to help developers understand why rendering stopped.
                                sLogger.e("[" + mName + "]  share context eglSwapBuffers swapError=" + swapError);

                                synchronized(sGLThreadManager) {
                                    mShareSurfaceIsBad = true;
                                    sGLThreadManager.notifyAll();
                                }
                                break;
                        }
                    } else {
                        if (LOG_RENDERER_DRAW_FRAME) {
                            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] can NOT draw share frame tid=" + getId());
                        }
                    }*/
                }
            } catch (RuntimeException e) {
                e.printStackTrace();
            } finally {
                /*
                 * clean-up everything...
                 */
                if (LOG_THREADS) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  clean-up everything tid=" + getId());
                }
                synchronized (sGLThreadManager) {
                    /*stopSurfaceTextureLocked();
                    stopGlContextLocked();
                    stopEglSurfaceLocked();
                    stopEglContextLocked();
                    mGlManager = null;
                    mEglManager = null;
                    mShareSurface = null;
                    mShareEglManager = null;*/
                }
            }
        }
        /*
        private int drawFrame() {
            int swapError = EGL14.EGL_SUCCESS;
            if (mEglManager != null && mGlManager != null) {
                if (LOG_RENDERER_DRAW_FRAME) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "] drawFrame tid=" + getId());
                }
                mEglManager.makeCurrent();
                if (mGlManager != null) {
                    mGlManager.setViewSize(mEglManager.getWidth(), mEglManager.getHeight());
                }
                int textureId = mGlManager.getTextureId();
                if (mAttachedTexture != -1 && textureId != mAttachedTexture) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "] drawFrame detach textureId=" + textureId 
                            + " mAttachedTexture=" + mAttachedTexture);
                    mSurfaceTexture.detachFromGLContext();
                }
                if (textureId != mAttachedTexture) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "] drawFrame attach textureId=" + textureId 
                            + " mAttachedTexture=" + mAttachedTexture);
                    mSurfaceTexture.attachToGLContext(textureId);
                    mAttachedTexture = textureId;
                }
                mSurfaceTexture.updateTexImage();
                mGlManager.updateTransform(mSurfaceTexture);
                mGlManager.drawFrame();
                mSurfaceTexture.detachFromGLContext();
                mAttachedTexture = -1;
                if (sLogger.isLoggable()) sLogger.d("[" + mName + "] drawFrame finish detach textureId=" + textureId 
                            + " mAttachedTexture=" + mAttachedTexture);
                swapError = mEglManager.swapBuffer();
            } else {
                if (LOG_RENDERER_DRAW_FRAME) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "] drawFrame can NOT tid=" + getId());
                }
            }
            return swapError;
        }

        private int drawShareFrame() {
            int swapError = EGL14.EGL_SUCCESS;
            if (mShareEglManager != null && mShareGlManager != null) {
                if (LOG_RENDERER_DRAW_FRAME) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "] drawShareFrame tid=" + getId());
                }
                mShareEglManager.makeCurrent();
                if (mShareGlManager != null) {
                    mShareGlManager.setViewSize(mShareEglManager.getWidth(), mShareEglManager.getHeight());
                }
                
                int textureId = mShareGlManager.getTextureId();
                if (mAttachedTexture != -1 && textureId != mAttachedTexture) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "] drawShareFrame detach textureId=" + textureId 
                            + " mAttachedTexture=" + mAttachedTexture);
                    mSurfaceTexture.detachFromGLContext();
                }
                if (textureId != mAttachedTexture) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "] drawShareFrame attach textureId=" + textureId 
                            + " mAttachedTexture=" + mAttachedTexture);
                    mSurfaceTexture.attachToGLContext(mShareGlManager.getTextureId());
                    mAttachedTexture = textureId;
                }
                if (!mHaveEglSurface) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "] drawShareFrame updateTexImage textureId=" + textureId 
                            + " mAttachedTexture=" + mAttachedTexture);
                    mSurfaceTexture.updateTexImage();
                }
                mShareGlManager.updateTransform(mSurfaceTexture);
                mShareGlManager.drawFrame();
                mOldts = mTs;
                mTs = mSurfaceTexture.getTimestamp();
                if (sLogger.isLoggable()) sLogger.e("[" + mName + "] mOldts="+ mOldts + " mTs="+mTs);
                if (mOldts != 0) {
                    if (sLogger.isLoggable()) sLogger.e("[" + mName + "] FPS: "+(1000000000/(mTs-mOldts)));
                }
                mShareEglManager.setPresentationTime(mTs);
                mSurfaceTexture.detachFromGLContext();
                mAttachedTexture = -1;
                if (sLogger.isLoggable()) sLogger.d("[" + mName + "] drawShareFrame finish detach textureId=" + textureId 
                        + " mAttachedTexture=" + mAttachedTexture);
                swapError = mShareEglManager.swapBuffer();
            } else {
                if (LOG_RENDERER_DRAW_FRAME) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "] drawShareFrame can NOT tid=" + getId());
                }
            }
            return swapError;
        }*/

        public boolean ableToDraw() {
            return mHaveEglContext && mHaveEglSurface && mHaveGlContext && readyToDraw();
        }

        private boolean readyToDraw() {
            if (LOG_THREADS) {
                if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  readyToDraw tid=" + getId()
                    + " mStopped: " + mStopped
                    + " mHasSurface: " + mHasSurface
                    //+ " mHasShareSurface: " + mHasShareSurface
                    + " mSurfaceIsBad: " + mSurfaceIsBad
                    + " mRequestRender: " + mRequestRender
                    + " mRenderMode: " + mRenderMode);
            }
            return (!mStopped) && (mHasSurface /*|| mHasShareSurface*/) && (!mSurfaceIsBad)
                //&& (mWidth > 0) && (mHeight > 0)
                && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
        }

        /*public void setInputSurface(Surface surface) {
            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] setInputSurface begin");
            //mInputSurface = surface;
            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] setInputSurface end");
        }*/

        /*public SurfaceTexture getSurfaceTexture() {
            synchronized(sGLThreadManager) {
                //return mGlManager != null ? mGlManager.getSurfaceTexture() : null;
                return mSurfaceTexture;
            }
        }*/

        /*public Surface getSurface() {
            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] getSurface");
            if (mSurface == null) {
                //if (mGlManager == null || getSurfaceTexture() == null) {
                if (mSurfaceTexture == null) {
                    sLogger.w("[" + mName + "] getSurface: SurfaceTexture is null.");
                    return null;
                }
                //mSurface = new Surface(getSurfaceTexture());
                mSurface = new Surface(mSurfaceTexture);
            }
            return mSurface;
        }*/

        /*public void addShareSurface(Surface surface) {
            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] addShareSurface begin");
            synchronized(sGLThreadManager) {
                if (LOG_SURFACE) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  addShareSurface tid=" + getId());
                }
                mShareSurface = surface;
                mHasShareSurface = true;
                mFinishedCreatingEglShareSurface = false;
                sGLThreadManager.notifyAll();
                while (mWaitingForShareSurface
                       &&!mFinishedCreatingEglShareSurface
                       && !mExited) {
                    try {
                        sGLThreadManager.wait();
                        if (LOG_THREADS) {
                            if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  addShareSurface wait end tid=" + getId());
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] addShareSurface end");
        }*/

        /*public void removeShareSurface() {
            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] removeShareSurface begin");
            synchronized(sGLThreadManager) {
                if (LOG_SURFACE) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  removeShareSurface tid=" + getId());
                }
                mHasShareSurface = false;
                sGLThreadManager.notifyAll();
                while((!mWaitingForShareSurface) && (!mExited)) {
                    try {
                        sGLThreadManager.wait();
                        if (LOG_THREADS) {
                            if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  removeShareSurface wait end tid=" + getId());
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
            if (sLogger.isLoggable()) sLogger.d("[" + mName + "] removeShareSurface end");
        }*/

        public void setRenderMode(int renderMode) {
            if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
                throw new IllegalArgumentException("renderMode");
            }
            synchronized(sGLThreadManager) {
                mRenderMode = renderMode;
                sGLThreadManager.notifyAll();
            }
        }

        public int getRenderMode() {
            synchronized(sGLThreadManager) {
                return mRenderMode;
            }
        }

        public void requestRender() {
            synchronized(sGLThreadManager) {
                if (LOG_RENDERER_DRAW_FRAME) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  requestRender tid=" + getId());
                }
                mRequestRender = true;
                sGLThreadManager.notifyAll();
            }
        }

        public void requestRenderAndNotify(Runnable finishDrawing) {
            synchronized(sGLThreadManager) {
                // If we are already on the GL thread, this means a client callback
                // has caused reentrancy, for example via updating the SurfaceView parameters.
                // We will return to the client rendering code, so here we don't need to
                // do anything.
                if (Thread.currentThread() == this) {
                    return;
                }

                mWantRenderNotification = true;
                mRequestRender = true;
                mRenderComplete = false;
                mFinishDrawingRunnable = finishDrawing;

                sGLThreadManager.notifyAll();
            }
        }

        public void surfaceCreated() {
            synchronized(sGLThreadManager) {
                if (LOG_THREADS) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  surfaceCreated tid=" + getId());
                }
                //if (mHasSurface) {
                    mSurfaceIsUpdate = true;
                //} else {
                    mHasSurface = true;
                //}
                mRequestRender = true;
                mFinishedCreatingEglSurface = false;
                sGLThreadManager.notifyAll();
                while (mWaitingForSurface
                       && !mFinishedCreatingEglSurface
                       && !mExited) {
                    try {
                        sGLThreadManager.wait();
                        if (LOG_THREADS) {
                            if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  surfaceCreated wait end tid=" + getId());
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }

        public void surfaceDestroyed() {
            synchronized(sGLThreadManager) {
                if (LOG_THREADS) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  surfaceDestroyed tid=" + getId());
                }
                mSurfaceIsUpdate = true;
                mHasSurface = false;
                sGLThreadManager.notifyAll();
                while((!mWaitingForSurface) && (!mExited)) {
                    try {
                        sGLThreadManager.wait();
                        if (LOG_THREADS) {
                            if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  surfaceDestroyed wait end tid=" + getId());
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }

        public boolean isStopped() {
            synchronized(sGLThreadManager) {
                return mStopped;
            }
        }

        public void onStop() {
            synchronized (sGLThreadManager) {
                if (LOG_STOP_START) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  onStop tid=" + getId());
                }
                mRequestStopped = true;
                sGLThreadManager.notifyAll();
                while ((! mExited) && (! mStopped)) {
                    if (LOG_STOP_START) {
                        if (sLogger.isLoggable()) sLogger.d("[" + mName + "] onStop waiting for mStopped.");
                    }
                    try {
                        sGLThreadManager.wait();
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }

        public void onStart() {
            synchronized (sGLThreadManager) {
                if (LOG_STOP_START) {
                    if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  onStart tid=" + getId());
                }
                mRequestStopped = false;
                mRequestRender = true;
                mRenderComplete = false;
                sGLThreadManager.notifyAll();
                while ((! mExited) && mStopped && (!mRenderComplete)) {
                    if (LOG_STOP_START) {
                        if (sLogger.isLoggable()) sLogger.d("[" + mName + "] onStart waiting for !mStopped.");
                    }
                    try {
                        sGLThreadManager.wait();
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }

        public void onWindowResize(int w, int h) {
            synchronized (sGLThreadManager) {
                mWidth = w;
                mHeight = h;
                mSizeChanged = true;
                mRequestRender = true;
                mRenderComplete = false;

                // If we are already on the GL thread, this means a client callback
                // has caused reentrancy, for example via updating the SurfaceView parameters.
                // We need to process the size change eventually though and update our EGLSurface.
                // So we set the parameters and return so they can be processed on our
                // next iteration.
                if (Thread.currentThread() == this) {
                    return;
                }

                sGLThreadManager.notifyAll();

                // Wait for thread to react to resize and render a frame
                while (! mExited && !mStopped && !mRenderComplete
                        && ableToDraw()) {
                    if (LOG_SURFACE) {
                        if (sLogger.isLoggable()) sLogger.d("[" + mName + "] onWindowResize waiting for render complete from tid=" + getId());
                    }
                    try {
                        sGLThreadManager.wait();
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }

        public boolean isExited() {
            sLogger.d("[" + mName + "]  isExited mExited="+ mExited + " tid=" + getId());
            synchronized(sGLThreadManager) {
                return mExited;
            }
        }

        public void requestExitAndWait() {
            // don't call this from GLThread thread or it is a guaranteed
            // deadlock!
            synchronized(sGLThreadManager) {
                mShouldExit = true;
                sGLThreadManager.notifyAll();
                while (! mExited) {
                    try {
                        sGLThreadManager.wait();
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }

        public void requestRelease() {
            requestReleaseGlContextLocked();
            requestReleaseEglContextLocked();
        }

        public void requestReleaseEglContextLocked() {
            synchronized(sGLThreadManager) {
                mShouldReleaseEglContext = true;
                sGLThreadManager.notifyAll();
            }
        }

        public void requestReleaseGlContextLocked() {
            synchronized(sGLThreadManager) {
                mShouldReleaseGlContext = true;
                sGLThreadManager.notifyAll();
            }
        }

        /**
         * Queue an "event" to be run on the GL rendering thread.
         * @param r the runnable to be run on the GL rendering thread.
         */
        public void queueEvent(Runnable r) {
            if (r == null) {
                throw new IllegalArgumentException("r must not be null");
            }
            synchronized(sGLThreadManager) {
                mEventQueue.add(r);
                sGLThreadManager.notifyAll();
            }
        }
/*
        @Override
        public void onFrameAvailable(SurfaceTexture surfaceTexture) {
            if (LOG_RENDERER_DRAW_FRAME) {
                if (sLogger.isLoggable()) sLogger.d("[" + mName + "]  onFrameAvailable tid=" + getId()
                        + " mHaveEglContext: " + mHaveEglContext
                        + " mHaveEglSurface: " + mHaveEglSurface
                        + " mHaveGlContext: " + mHaveGlContext
                        + " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface
                        + " mStopped: " + mStopped
                        + " mHasSurface: " + mHasSurface
                        //+ " mHasSurfaceTexture: " + mHasSurfaceTexture
                        + " mSurfaceIsUpdate: " + mSurfaceIsUpdate
                        + " mSurfaceIsBad: " + mSurfaceIsBad
                        + " mWaitingForSurface: " + mWaitingForSurface
                        + " mWidth: " + mWidth
                        + " mHeight: " + mHeight
                        + " mRequestRender: " + mRequestRender
                        + " mRenderMode: " + mRenderMode
                        //+ " mInputSurface: " + mInputSurface);
                        );

                //if (mInputSurface!=null && mInputSurface.isValid()) {
                //    sLogger.d("[" + mName + "]  onFrameAvailable mInputSurface Valid tid=" + getId());
                //} else {
                //    sLogger.d("[" + mName + "]  onFrameAvailable mInputSurface Invalid tid=" + getId());
                //}
            }

            requestRender();
        }*/
/*
        private FvlRenderTarget getRenderTarget() {
            if (mRenderTarget == null) {
                mRenderTarget = FvlRenderTarget.newTarget(1, 1);
            }
            return mRenderTarget;
        }

        private void onCleanupContext(EGLContext context) {
            FvlTextureSource texture = mTextureSources.get(context);
            FvlTextureRenderer renderer = mTextureRenderers.get(context);
            if (texture != null) {
                texture.release();
                mTextureSources.remove(context);
            }
            if (renderer != null) {
                mTextureRenderers.remove(context);
            }
            SurfaceTexture surfaceTex = mTargetSurfaceTextures.get(context);
            if (surfaceTex != null) {
                surfaceTex.release();
                mTargetSurfaceTextures.remove(context);
            }
        }

        private FvlTextureSource textureForContext(EGLContext context) {
            FvlTextureSource texture = mTextureSources.get(context);
            if (texture == null) {
                texture = createTextureTarget();
                mTextureSources.put(context, texture);
            }
            return texture;
        }

        private FvlTextureRenderer rendererForContext(EGLContext context) {
            FvlTextureRenderer renderer = mTextureRenderers.get(context);
            if (renderer == null) {
                renderer = createTextureRenderer();
                mTextureRenderers.put(context, renderer);
            }
            return renderer;
        }

        //线程启动时，创建多个SurfaceTexture，需要egl make current
        //但是这个是用EGLContext创建的，是不是改成用index比较好
        private SurfaceTexture surfaceTextureForContext(EGLContext context) {
            SurfaceTexture surfTex = mSourceSurfaceTextures.get(context);
            if (surfTex == null) {
                FvlTextureSource texture = textureForContext(context);
                if (texture != null) {
                    surfTex = new SurfaceTexture(texture.getTextureId());
                    mSourceSurfaceTextures.put(context, surfTex);
                }
            }
            return surfTex;
        }
*/
        /*private synchronized void updateSurfaceTextureSizes() {
            for (SurfaceTexture clientTexture : mSourceSurfaceTextures.values()) {
                clientTexture.setDefaultBufferSize(mOutWidth, mOutHeight);
            }
        }*/

        //外部传入输出Surface时，创建EGL环境，并添加到hashmap里
        /*private FvlRenderTarget renderTargetFor(Surface surface) {
            FvlRenderTarget target = mRenderTargets.get(surface);
            if (target == null) {
                target = FvlRenderTarget.currentTarget().forSurface(surface);
                mRenderTargets.put(surface, target);
            }
            return target;
        }*/

        /*private void distributeFrames() {
            //updateTransform(getCopyShader());
            //updateShaderTargetRect(getCopyShader());

            for (Surface surface : mTargetSurfaces.values()) {
                FvlRenderTarget target = renderTargetFor(surface);
                target.focus();
                getCopyShader().process(mTextureSource,
                                        clientTarget,
                                        mOutWidth,
                                        mOutHeight);
                GLToolbox.checkGlError("distribute frames");
                target.swapBuffers();
            }
        }*/
/*
        private FvlTextureSource createTextureTarget() {
            FvlTextureSource texture = FvlTextureSource.newExternalTexture();
            texture.setParameterf(GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
            texture.setParameterf(GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
            texture.setParameteri(GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
            texture.setParameteri(GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
            return texture;
        }

        private void updateSurfaceTexture() {
            mSurfaceTexture.attachToGLContext(mTextureSource.getTextureId());
            mSurfaceTexture.updateTexImage();
            mSurfaceTexture.detachFromGLContext();
        }

        private FvlTextureRenderer createTextureRenderer() {
            return new FvlTextureRenderer();
        }

        public synchronized void release() {
            if (mTextureSource != null) {
                mTextureSource.release();
                mTextureSource = null;
            }
            if (mSurfaceTexture != null) {
                mSurfaceTexture.release();
                mSurfaceTexture = null;
            }
        }*/
/*
        protected void setupSurfaceTexture() {
            if (mTextureSource == null) {
                mTextureSource = FvlTextureSource.newExternalTexture();
            }
            mTextureSource.bind();
            if (mTextureRender == null) {
                mTextureRender = new FvlTextureRenderer();
            }
            if (mSurfaceTexture == null) {
                mSurfaceTexture = new SurfaceTexture(mTextureSource.getTextureId());
                //mSurfaceTexture.setOnFrameAvailableListener(mOnFrameAvailableListener);
                mSurfaceTexture.setOnFrameAvailableListener(this);
                mSurfaceTexture.detachFromGLContext();
            }
        }*/

        /*private SurfaceTexture.OnFrameAvailableListener mOnFrameAvailableListener =
                new SurfaceTexture.OnFrameAvailableListener() {
            @Override
            public void onFrameAvailable(SurfaceTexture surfaceTexture) {
            }
        };*/

        private String mTag = null;
        private String mName = null;

        // Once the thread is started, all accesses to the following member
        // variables are protected by the sGLThreadManager monitor
        private boolean mShouldExit;
        private boolean mExited;
        private boolean mRequestStopped;
        private boolean mStopped;
        private boolean mHasSurface;
        //private boolean mHasSurfaceTexture;
        //private boolean mHasShareSurface;
        private boolean mSurfaceIsUpdate;
        private boolean mSurfaceIsBad;
        //private boolean mShareSurfaceIsBad;
        private boolean mWaitingForSurface;
        //private boolean mWaitingForShareSurface;
        private boolean mHaveEglContext;
        private boolean mHaveEglSurface;
        //private boolean mHaveEglShareContext;
        //private boolean mHaveEglShareSurface;
        private boolean mHaveGlContext;
        //private boolean mHaveShareGlContext;
        private boolean mFinishedCreatingEglSurface;
        //private boolean mFinishedCreatingEglShareSurface;
        private boolean mShouldReleaseEglContext;
        private boolean mShouldReleaseGlContext;
        private int mWidth;
        private int mHeight;
        private int mRenderMode;
        private boolean mRequestRender;
        private boolean mWantRenderNotification;
        private boolean mRenderComplete;
        private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
        private boolean mSizeChanged = true;
        private Runnable mFinishDrawingRunnable = null;

        // End of member variables protected by the sGLThreadManager monitor.

        // The surface in which the preview is rendered
        //private FvlSurfaceManager mEglManager = null;

        // The input surface of the MediaCodec
       // private FvlSurfaceManager mShareEglManager = null;
        //private FvlTextureManager mShareGlManager = null;
        //private Surface mShareSurface = null;

        /**
         * Set once at thread construction time, nulled out when the parent view is garbage
         * called. This weak reference allows the GLSurfaceView to be garbage collected while
         * the GLThread is still alive.
         */
        private WeakReference<FvlVideoConsumerBase> mFvlVideoConsumerBaseWeakRef;


        // Handles the rendering of the SurfaceTexture we got
        // from the camera, onto a Surface
        //private FvlTextureManager mGlManager = null;
        //private SurfaceTexture mSurfaceTexture = null;
        //private Surface mSurface = null;

        /** Map of GLSL shaders (one for each target context) */
        //protected HashMap<EGLContext, FvlTextureRenderer> mTextureRenderers
        //    = new HashMap<EGLContext, FvlTextureRenderer>();

        /** Map of target textures (one for each target context) */
        //protected HashMap<EGLContext, FvlTextureSource> mTextureSources
        //    = new HashMap<EGLContext, FvlTextureSource>();

        /** Map of target surface texture (one for each target context) */
        //protected HashMap<EGLContext, SurfaceTexture> mSourceSurfaceTextures
        //    = new HashMap<EGLContext, SurfaceTexture>();

        /** Map of RenderTargets for output Surface, include SurfaceView and MediaCodec inputsurface */
        //protected HashMap<Surface, FvlRenderTarget> mRenderTargets
        //    = new HashMap<Surface, FvlRenderTarget>();

        /** Map of source surface textures (one for each render target) */
        //protected HashMap<FvlRenderTarget, SurfaceTexture> mTargetSurfaceTextures
        //    = new HashMap<FvlRenderTarget, SurfaceTexture>();

        //private FvlRenderTarget mRenderTarget;
        //private FvlRenderTarget mUIRenderTarget;
        //private FvlTextureSource mTextureSource = null;
        //private FvlTextureRenderer mTextureRender = null;
        //private SurfaceTexture mSurfaceTexture1 = null;

    }

    private void checkRenderThreadState() {
        if (mGLThread != null) {
            throw new IllegalStateException(
                    "setRenderer has already been called for this instance.");
        }
    }

    private static class GLThreadManager {

        public synchronized void threadExiting(GLThread thread) {
            if (LOG_THREADS) {
                sLogger.d("FvlVideoConsumerBase: exiting tid=" +  thread.getId());
            }
            thread.mStopped = true;
            thread.mExited = true;
            notifyAll();
        }

        /*
         * Releases the EGL context. Requires that we are already in the
         * sGLThreadManager monitor when this is called.
         */
        public void releaseEglContextLocked(GLThread thread) {
            notifyAll();
        }

        /*
         * Releases the GL context. Requires that we are already in the
         * sGLThreadManager monitor when this is called.
         */
        public void releaseGlContextLocked(GLThread thread) {
            notifyAll();
        }
    }
}
